using System;
using System.Globalization;
using System.Security.Cryptography;
using System.Text;
using System.Web.Routing;
using Nop.Core;
using Nop.Core.Domain.Directory;
using Nop.Core.Domain.Orders;
using Nop.Core.Domain.Payments;
using Nop.Core.Plugins;
using Nop.Plugin.Payments.WalletOne.Controllers;
using Nop.Services.Configuration;
using Nop.Services.Customers;
using Nop.Services.Directory;
using Nop.Services.Localization;
using Nop.Services.Payments;
using Nop.Web.Framework;

namespace Nop.Plugin.Payments.WalletOne
{
    /// <summary>
	/// WalletOne payment processor
    /// </summary>
    public class WalletOnePaymentProcessor : BasePlugin, IPaymentMethod
    {
        #region Fields

        private readonly WalletOnePaymentSettings _walletOnePaymentSettings;
		private readonly ILocalizationService _localizationService;
		private readonly ICurrencyService _currencyService;
        private readonly CurrencySettings _currencySettings;
		private readonly ICustomerService _customerService;
		private readonly ISettingService _settingService;
        private readonly IWebHelper _webHelper;

        #endregion

        #region Ctor

		public WalletOnePaymentProcessor(WalletOnePaymentSettings walletOnePaymentSettings,
			ILocalizationService localizationService,
			ICurrencyService currencyService,
			CurrencySettings currencySettings, ICustomerService customerService, ISettingService settingService, IWebHelper webHelper)
        {
			this._walletOnePaymentSettings = walletOnePaymentSettings;
			this._localizationService = localizationService;
			this._currencyService = currencyService;
            this._currencySettings = currencySettings;
			this._customerService = customerService;
			this._settingService = settingService;
            this._webHelper = webHelper;
        }

        #endregion

		#region Utilities

		/// <summary>
		/// Gets WalletOne URL
		/// </summary>
		/// <returns></returns>
		private string GetWalletOneUrl()
		{
			return "https://merchant.w1.ru/checkout/default.aspx";
		}

		#endregion

		#region Methods

		/// <summary>
        /// Process a payment
        /// </summary>
        /// <param name="processPaymentRequest">Payment info required for an order processing</param>
        /// <returns>Process payment result</returns>
        public ProcessPaymentResult ProcessPayment(ProcessPaymentRequest processPaymentRequest)
        {
			var result = new ProcessPaymentResult();
			result.NewPaymentStatus = PaymentStatus.Pending;
			return result;
		}

        /// <summary>
        /// Post process payment (used by payment gateways that require redirecting to a third-party URL)
        /// </summary>
        /// <param name="postProcessPaymentRequest">Payment info required for an order processing</param>
        public void PostProcessPayment(PostProcessPaymentRequest postProcessPaymentRequest)
        {
			var post = new RemotePost();

			post.FormName = "frmWalletOne";
			post.Url = GetWalletOneUrl();
			post.AcceptCharset = "UTF-8";
			post.NewInputForEachValue = true;
			post.Method = "POST";

			//!!! Add parameters in alfabetical order
			post.Add("WMI_CURRENCY_ID", WalletOneHelper.GetCurrencyNumericCode(postProcessPaymentRequest.Order.CustomerCurrencyCode));
			post.Add("WMI_DESCRIPTION", String.Format(_localizationService.GetResource("Plugins.Payments.WalletOne.Description"), postProcessPaymentRequest.Order.Id.ToString()));
			post.Add("WMI_FAIL_URL", _webHelper.GetStoreLocation(false));
			post.Add("WMI_MERCHANT_ID", _walletOnePaymentSettings.MerchantId);
			var orderTotalInCustomerCurrency = _currencyService.ConvertCurrency(postProcessPaymentRequest.Order.OrderTotal, postProcessPaymentRequest.Order.CurrencyRate);
			post.Add("WMI_PAYMENT_AMOUNT", String.Format(CultureInfo.InvariantCulture, "{0:0.00}", orderTotalInCustomerCurrency));
			post.Add("WMI_PAYMENT_NO", postProcessPaymentRequest.Order.Id.ToString());

			if (!String.IsNullOrEmpty(_walletOnePaymentSettings.PaymentMethodsOffered))
			{
				string[] services = _walletOnePaymentSettings.PaymentMethodsOffered.Split(new Char[] { ':' });
				Array.Sort(services);

				foreach (string service in services)
					post.Add("WMI_PTENABLED", service);
			}

			post.Add("WMI_RECIPIENT_LOGIN", postProcessPaymentRequest.Order.BillingAddress.Email);
			post.Add("WMI_SUCCESS_URL", String.Format("{0}Plugins/PaymentWalletOne/Return", _webHelper.GetStoreLocation(false)));

			StringBuilder signatureData = new StringBuilder();
			foreach (string key in post.Params.Keys)
			{
				string[] values = post.Params.GetValues(key);
				Array.Sort(values);

				foreach (string value in values)
					signatureData.Append(value);
			}

			string message = signatureData.ToString() + _walletOnePaymentSettings.SecretKey;
			Byte[] bytes = Encoding.GetEncoding(1251).GetBytes(message);
			Byte[] hash = new MD5CryptoServiceProvider().ComputeHash(bytes);
			string signature = Convert.ToBase64String(hash);
			post.Add("WMI_SIGNATURE", signature);

			post.Post();
		}

        /// <summary>
        /// Gets additional handling fee
        /// </summary>
        /// <returns>Additional handling fee</returns>
        public decimal GetAdditionalHandlingFee()
        {
			return _walletOnePaymentSettings.AdditionalFee;
        }

        /// <summary>
        /// Captures payment
        /// </summary>
        /// <param name="capturePaymentRequest">Capture payment request</param>
        /// <returns>Capture payment result</returns>
        public CapturePaymentResult Capture(CapturePaymentRequest capturePaymentRequest)
        {
            var result = new CapturePaymentResult();
			result.AddError("Capture method not supported");
			return result;
        }

        /// <summary>
        /// Refunds a payment
        /// </summary>
        /// <param name="refundPaymentRequest">Request</param>
        /// <returns>Result</returns>
        public RefundPaymentResult Refund(RefundPaymentRequest refundPaymentRequest)
        {
            var result = new RefundPaymentResult();
			result.AddError("Refund method not supported");
			return result;
        }

        /// <summary>
        /// Voids a payment
        /// </summary>
        /// <param name="voidPaymentRequest">Request</param>
        /// <returns>Result</returns>
        public VoidPaymentResult Void(VoidPaymentRequest voidPaymentRequest)
        {
            var result = new VoidPaymentResult();
			result.AddError("Void method not supported");
			return result;
        }

        /// <summary>
        /// Process recurring payment
        /// </summary>
        /// <param name="processPaymentRequest">Payment info required for an order processing</param>
        /// <returns>Process payment result</returns>
        public ProcessPaymentResult ProcessRecurringPayment(ProcessPaymentRequest processPaymentRequest)
        {
            var result = new ProcessPaymentResult();
            result.AddError("Recurring payment not supported");
            return result;
        }

        /// <summary>
        /// Cancels a recurring payment
        /// </summary>
        /// <param name="cancelPaymentRequest">Request</param>
        /// <returns>Result</returns>
        public CancelRecurringPaymentResult CancelRecurringPayment(CancelRecurringPaymentRequest cancelPaymentRequest)
        {
            var result = new CancelRecurringPaymentResult();
            result.AddError("Recurring payment not supported");
            return result;
        }

        /// <summary>
        /// Gets a value indicating whether customers can complete a payment after order is placed but not completed (for redirection payment methods)
        /// </summary>
        /// <param name="order">Order</param>
        /// <returns>Result</returns>
        public bool CanRePostProcessPayment(Order order)
        {
            if (order == null)
                throw new ArgumentNullException("order");

			//WalletOne is the redirection payment method
            //It also validates whether order is also paid (after redirection) so customers will not be able to pay twice
            
            //payment status should be Pending
            if (order.PaymentStatus != PaymentStatus.Pending)
                return false;

            //let's ensure that at least 1 minute passed after order is placed
            if ((DateTime.UtcNow - order.CreatedOnUtc).TotalMinutes < 1)
                return false;

            return true;
        }

        /// <summary>
        /// Gets a route for provider configuration
        /// </summary>
        /// <param name="actionName">Action name</param>
        /// <param name="controllerName">Controller name</param>
        /// <param name="routeValues">Route values</param>
        public void GetConfigurationRoute(out string actionName, out string controllerName, out RouteValueDictionary routeValues)
        {
            actionName = "Configure";
			controllerName = "PaymentWalletOne";
			routeValues = new RouteValueDictionary() { { "Namespaces", "Nop.Plugin.Payments.WalletOne.Controllers" }, { "area", null } };
        }

        /// <summary>
        /// Gets a route for payment info
        /// </summary>
        /// <param name="actionName">Action name</param>
        /// <param name="controllerName">Controller name</param>
        /// <param name="routeValues">Route values</param>
        public void GetPaymentInfoRoute(out string actionName, out string controllerName, out RouteValueDictionary routeValues)
        {
            actionName = "PaymentInfo";
			controllerName = "PaymentWalletOne";
			routeValues = new RouteValueDictionary() { { "Namespaces", "Nop.Plugin.Payments.WalletOne.Controllers" }, { "area", null } };
        }

        public Type GetControllerType()
        {
			return typeof(PaymentWalletOneController);
        }

        public override void Install()
        {
			var settings = new WalletOnePaymentSettings()
            {
				MerchantId = "123456789012",
				SecretKey = "EnterYourSecretKey",
				PaymentMethodsOffered = "BankTransferRUB:LiqPayRUB:RbkMoneyRUB:SvyaznoyBankRUB:WalletOneRUB:YandexMoneyRUB",
                AdditionalFee = 0,
            };
            _settingService.SaveSetting(settings);

			//locales
			this.AddOrUpdatePluginLocaleResource("Plugins.Payments.WalletOne.Notes", "If you're using this gateway, ensure that your primary store currency is supported by WalletOne.");
			this.AddOrUpdatePluginLocaleResource("Plugins.Payments.WalletOne.RedirectNote", "You will be redirected to WalletOne site to complete the order.");
			this.AddOrUpdatePluginLocaleResource("Plugins.Payments.WalletOne.Description", "Payment for order #{0}");
			this.AddOrUpdatePluginLocaleResource("Plugins.Payments.WalletOne.Fields.MerchantId", "Merchant ID");
			this.AddOrUpdatePluginLocaleResource("Plugins.Payments.WalletOne.Fields.MerchantId.Hint", "Specify Merchant identifier.");
			this.AddOrUpdatePluginLocaleResource("Plugins.Payments.WalletOne.Fields.SecretKey", "SecretKey");
			this.AddOrUpdatePluginLocaleResource("Plugins.Payments.WalletOne.Fields.SecretKey.Hint", "Specify MD5 hashed secret key. You can get it in Settings->Web-store section of your WalletOne profile page.");
			this.AddOrUpdatePluginLocaleResource("Plugins.Payments.WalletOne.Fields.PaymentMethods", "Payment methods");
			this.AddOrUpdatePluginLocaleResource("Plugins.Payments.WalletOne.Fields.PaymentMethods.Hint", "Select payment methods.");
			this.AddOrUpdatePluginLocaleResource("Plugins.Payments.WalletOne.Fields.AdditionalFee", "Additional fee");
			this.AddOrUpdatePluginLocaleResource("Plugins.Payments.WalletOne.Fields.AdditionalFee.Hint", "Enter additional fee to charge your customers.");
            base.Install();
        }

		public override void Uninstall()
		{
			//locales
			this.DeletePluginLocaleResource("Plugins.Payments.WalletOne.Notes");
			this.DeletePluginLocaleResource("Plugins.Payments.WalletOne.RedirectNote");
			this.DeletePluginLocaleResource("Plugins.Payments.WalletOne.Description");
			this.DeletePluginLocaleResource("Plugins.Payments.WalletOne.Fields.MerchantId");
			this.DeletePluginLocaleResource("Plugins.Payments.WalletOne.Fields.MerchantId.Hint");
			this.DeletePluginLocaleResource("Plugins.Payments.WalletOne.Fields.SecretKey");
			this.DeletePluginLocaleResource("Plugins.Payments.WalletOne.Fields.SecretKey.Hint");
			this.DeletePluginLocaleResource("Plugins.Payments.WalletOne.Fields.PaymentMethods");
			this.DeletePluginLocaleResource("Plugins.Payments.WalletOne.Fields.PaymentMethods.Hint");
			this.DeletePluginLocaleResource("Plugins.Payments.WalletOne.Fields.AdditionalFee");
			this.DeletePluginLocaleResource("Plugins.Payments.WalletOne.Fields.AdditionalFee.Hint");

			base.Uninstall();
		}

        #endregion

        #region Properies

        /// <summary>
        /// Gets a value indicating whether capture is supported
        /// </summary>
        public bool SupportCapture
        {
            get
            {
                return false;
            }
        }

        /// <summary>
        /// Gets a value indicating whether partial refund is supported
        /// </summary>
        public bool SupportPartiallyRefund
        {
            get
            {
				return false;
            }
        }

        /// <summary>
        /// Gets a value indicating whether refund is supported
        /// </summary>
        public bool SupportRefund
        {
            get
            {
				return false;
            }
        }

        /// <summary>
        /// Gets a value indicating whether void is supported
        /// </summary>
        public bool SupportVoid
        {
            get
            {
				return false;
            }
        }

        /// <summary>
        /// Gets a recurring payment type of payment method
        /// </summary>
        public RecurringPaymentType RecurringPaymentType
        {
            get
            {
                return RecurringPaymentType.NotSupported;
            }
        }

        /// <summary>
        /// Gets a payment method type
        /// </summary>
        public PaymentMethodType PaymentMethodType
        {
            get
            {
                return PaymentMethodType.Redirection;
            }
        }

        #endregion
    }
}
